﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using System.Collections.Specialized;

namespace SharePointFarmInfoTimerJob
{
    /// <summary>
    /// SPFarmTopology
    /// </summary>
    class SPFarmTopology
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="siteWebUrl"></param>
        /// <param name="spListName"></param>
        public SPFarmTopology(string siteWebUrl, string spListName)
        {
            using (SPSite site = new SPSite(siteWebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    //Try/create the SPList
                    SPList spFarmTopologyList = this.TryCreateList(web, spListName);
                    //Create/Update the info
                    this.FetchSPFarmInfo(spFarmTopologyList);
                }
            }
        }


        /// <summary>
        /// Try and Create SPList if it doesnot exist
        /// </summary>
        /// <param name="web">SPweb object</param>
        /// <param name="spListName">Sharepoint List name</param>
        /// <returns>SPList object</returns>
        public SPList TryCreateList(SPWeb web, string spListName)
        {
            //Check whether the List exists
            SPList spFarmTopologyList = web.Lists.TryGetList(spListName);

            //Create List if it doesn't exist
            if (spFarmTopologyList == null)
            {
                web.Lists.Add(spListName, Global.spListDescription, SPListTemplateType.GenericList);
                spFarmTopologyList = web.Lists.TryGetList(spListName);
            }

            //Instance for Listview
            SPView spFarmTopologyListView = spFarmTopologyList.DefaultView;

            //Add the columns if they are not present
            foreach (string columnName in Global.spListColumns)
            {
                if (!spFarmTopologyList.Fields.Cast<SPField>().AsEnumerable().
                    Any(fieldName => fieldName.Title.Equals(columnName, StringComparison.OrdinalIgnoreCase)))
                {
                    if (Global.spIntListColumns.Contains(columnName))
                    {
                        SPFieldNumber numberField = (SPFieldNumber)spFarmTopologyList.Fields.CreateNewField(SPFieldType.Number.ToString(), columnName);
                        numberField.DisplayFormat = SPNumberFormatTypes.Automatic;
                        spFarmTopologyList.Fields.Add(numberField);
                    }
                    else if (Global.spIntListColumnsInPercentage.Contains(columnName))
                    {
                        SPFieldNumber numberField = (SPFieldNumber)spFarmTopologyList.Fields.CreateNewField(SPFieldType.Number.ToString(), columnName);
                        numberField.DisplayFormat = SPNumberFormatTypes.Automatic;
                        numberField.MaximumValue = 1;
                        numberField.MinimumValue = 0;
                        numberField.ShowAsPercentage = true;
                        spFarmTopologyList.Fields.Add(numberField);
                    }
                    else if (Global.spBoolListColumns.Contains(columnName))
                    {
                        spFarmTopologyList.Fields.Add(columnName, SPFieldType.Boolean, false);
                    }
                    else
                    {
                        spFarmTopologyList.Fields.Add(columnName, SPFieldType.Text, false);
                    }
                    spFarmTopologyListView.ViewFields.Add(columnName);
                }
            }

            if (!spFarmTopologyListView.ViewFields.ToStringCollection().Contains(Global.modified))
            {
                spFarmTopologyListView.ViewFields.Add(Global.modified);
            }

            //Delete the Title column
            if (spFarmTopologyListView.ViewFields.Exists(Global.titleColumnName))
            {
                spFarmTopologyListView.ViewFields.Delete(Global.titleColumnName);
            }

            //Set the query for group and orderby
            spFarmTopologyListView.Query = Global.listViewQuery;
            //Update the SPList
            spFarmTopologyListView.Update();

            //Create SharePoint Farm Server List View
            this.CreateSPListView(spFarmTopologyList, Global.FarmViewColumns, Global.sharePointFarmListView,
                String.Format(Global.customListViewQuery, ObjectType.Farm.ToString()));
            //Create SharePoint Farm Server List View
            this.CreateSPListView(spFarmTopologyList, Global.ServerViewColumns, Global.sharePointFarmServerListView,
                String.Format(Global.customListViewQuery, ObjectType.Server.ToString()));
            //Create SharePoint Farm Service List View
            this.CreateSPListView(spFarmTopologyList, Global.ServiceViewColumns, Global.sharePointFarmServiceListView,
                String.Format(Global.customListViewQuery, Global.stringValueOf(ObjectType.SharePointService)));

            //Return the SPList
            return spFarmTopologyList;
        }

        /// <summary>
        /// Create SharePoint List View
        /// </summary>
        private void CreateSPListView(SPList sharePointList, string[] viewFieldColumns, string listViewTitle, string filterQuery)
        {
            StringCollection viewFields = new StringCollection();
            viewFields.AddRange(viewFieldColumns);

            if (!sharePointList.Views.Cast<SPView>().Any(view => view.Title.Equals(listViewTitle, StringComparison.OrdinalIgnoreCase)))
            {
                sharePointList.Views.Add(listViewTitle, viewFields, filterQuery, Int32.MaxValue, true, false);
            }
        }

        /// <summary>
        /// Fetch the Sharepoint farm server and their service information
        /// </summary>
        /// <param name="spFarmTopologyList">SPList</param>
        private void FetchSPFarmInfo(SPList spFarmTopologyList)
        {
            //Initialize the objects
            string serverName = String.Empty;

            string uniqueid = String.Empty;
            int index = 1;

            SPFarm sharepointFarm = SPFarm.Local;

            //Add the SharePoint farm details
            AddFarmDetails(spFarmTopologyList, ref index, sharepointFarm);

            //Iterate through the farm servers
            foreach (SPServer server in sharepointFarm.Servers)
            {
                //Add SharePoint server details
                AddServerDetails(spFarmTopologyList, ref serverName, ref index, server);

                //Iterate through the service instaces
                foreach (SPServiceInstance serviceInstance in server.ServiceInstances)
                {
                    AddServiceDetails(spFarmTopologyList, serverName, index, serviceInstance);

                    //Increment the counter
                    index++;
                }
            }
        }

        /// <summary>
        /// Add SharePoint server details
        /// </summary>
        /// <param name="spFarmTopologyList">SharePoint List</param>
        /// <param name="serverName">SharePoint server name</param>
        /// <param name="farmName">SharePoint farm name</param>
        /// <param name="index">Index</param>
        /// <param name="server">SharePoint server</param>
        private void AddServerDetails(SPList spFarmTopologyList, ref string serverName, ref int index, SPServer server)
        {
            //Fetch the server name
            serverName = server.Name;
            string uniqueid = server.Id.ToString();

            //Fetch the task manager information
            WindowsTaskInfo taskMgrInfo = new WindowsTaskInfo(serverName);


            SPListItem spFarmTopologyListItem = this.RetrieveSPFarmTopologyListItem(spFarmTopologyList, uniqueid);


            //SharePoint server details
            spFarmTopologyListItem[Global.indexColumnName] = index;
            spFarmTopologyListItem[Global.objectTypeColumnName] = ObjectType.Server.ToString();
            spFarmTopologyListItem[Global.serverColumnName] = serverName;
            spFarmTopologyListItem[Global.statusColumnName] = server.Status.ToString();
            spFarmTopologyListItem[Global.versionColumnName] = server.Version.ToString();
            spFarmTopologyListItem[Global.needsUpgradeColumnName] = server.NeedsUpgrade;
            spFarmTopologyListItem[Global.canUpgradeColumnName] = server.CanUpgrade;
            spFarmTopologyListItem[Global.serverRoleColumnName] = server.Role.ToString();
            spFarmTopologyListItem[Global.uniqueid] = uniqueid;
            spFarmTopologyListItem[Global.cpuUsageColumnName] = taskMgrInfo.CPUUsage;
            spFarmTopologyListItem[Global.freeSpaceDetailsColumnName] = taskMgrInfo.SystemDirectorySpaceUsage;
            spFarmTopologyListItem[Global.freeDiskSpace] = taskMgrInfo.DiskFreeSpace;
            spFarmTopologyListItem[Global.diskSpaceCapacity] = taskMgrInfo.DiskCapacity;
            spFarmTopologyListItem[Global.freeSpacePercentColumnName] = taskMgrInfo.SystemDirectoryFreeSpaceInPercentage;
            spFarmTopologyListItem[Global.memoryUsageDetailsColumnName] = taskMgrInfo.MemoryUsage;
            spFarmTopologyListItem[Global.freeMemorySpace] = taskMgrInfo.FreeMemory;
            spFarmTopologyListItem[Global.memoryCapacity] = taskMgrInfo.TotalMemory;
            spFarmTopologyListItem[Global.memoryUsagePercentColumnName] = taskMgrInfo.MemoryUsageInPercentage;

            //Update the value
            spFarmTopologyListItem.Update();

            index++;
        }

        /// <summary>
        /// Add Sharepoint farm details
        /// </summary>
        /// <param name="spFarmTopologyList">SharePoint List</param>
        /// <param name="farmName">SharePoint Farm Name</param>
        /// <param name="index">Index</param>
        /// <param name="sharepointFarm">Sharepoint Farm</param>
        private void AddFarmDetails(SPList spFarmTopologyList, ref int index, SPFarm sharepointFarm)
        {
            string configDBName = String.Empty;
            double configDBSize = 0;
            string uniqueid = sharepointFarm.Id.ToString();

            var dbService = SPFarm.Local.Services.Where(service => service.GetType().Equals(typeof(SPDatabaseService))).
                            FirstOrDefault();

            if (dbService != null)
            {
                foreach (SPDatabaseServiceInstance instance in dbService.Instances)
                {
                    // enumerate the databases within this service instance
                    foreach (SPDatabase database in instance.Databases)
                    {
                        if (database.GetType().ToString().Equals("Microsoft.SharePoint.Administration.SPConfigurationDatabase"))
                        {
                            configDBName = database.Name;
                            configDBSize = Global.ConvertBytesToGB(database.DiskSizeRequired);
                            break;
                        }
                    }
                }
            }

            SPListItem spFarmTopologyListItem = this.RetrieveSPFarmTopologyListItem(spFarmTopologyList, uniqueid);

            spFarmTopologyListItem[Global.indexColumnName] = index.ToString();
            spFarmTopologyListItem[Global.objectTypeColumnName] = ObjectType.Farm.ToString();
            spFarmTopologyListItem[Global.versionColumnName] = sharepointFarm.BuildVersion.ToString();
            spFarmTopologyListItem[Global.configDBNameColumnName] = configDBName;
            spFarmTopologyListItem[Global.configDBSizeColumnName] = configDBSize;
            spFarmTopologyListItem[Global.uniqueid] = uniqueid;
            spFarmTopologyListItem[Global.needsUpgradeColumnName] = sharepointFarm.NeedsUpgrade;
            spFarmTopologyListItem[Global.canUpgradeColumnName] = sharepointFarm.CanUpgrade;

            //Update the value
            spFarmTopologyListItem.Update();

            index++;
        }

        /// <summary>
        /// Add Sharepoint service details
        /// </summary>
        /// <param name="spFarmTopologyList">SharePoint List</param>
        /// <param name="farmName">SharePoint Farm Name</param>
        /// <param name="serverName">SharePoint server name</param>
        /// <param name="counter">Index counter</param>
        /// <param name="serviceInstance">SharePoint service instance</param>
        private void AddServiceDetails(SPList spFarmTopologyList, string serverName, int counter, SPServiceInstance serviceInstance)
        {
            //Fetch the unique guid
            string uniqueid = serviceInstance.Id.ToString();

            //Get the row based on the guid
            //If it doesnt exist, create a new row
            SPListItem spFarmTopologyListItem = this.RetrieveSPFarmTopologyListItem(spFarmTopologyList, uniqueid);

            //Set the value
            spFarmTopologyListItem[Global.indexColumnName] = counter.ToString();
            spFarmTopologyListItem[Global.objectTypeColumnName] = Global.stringValueOf(ObjectType.SharePointService);
            spFarmTopologyListItem[Global.needsUpgradeColumnName] = serviceInstance.NeedsUpgrade;
            spFarmTopologyListItem[Global.canUpgradeColumnName] = serviceInstance.CanUpgrade;
            spFarmTopologyListItem[Global.versionColumnName] = serviceInstance.Version.ToString();
            spFarmTopologyListItem[Global.serverColumnName] = serverName;
            spFarmTopologyListItem[Global.serviceColumnName] = serviceInstance.Service.TypeName;
            spFarmTopologyListItem[Global.statusColumnName] = serviceInstance.Status.ToString();
            spFarmTopologyListItem[Global.uniqueid] = uniqueid;

            //Update the value
            spFarmTopologyListItem.Update();
        }

        /// <summary>
        /// Retrieve the SharePoint List item. Try to retrieve the List item based on the unique id. If it doesn't exist create a new item.
        /// </summary>
        /// <param name="spFarmTopologyList">SharePoint List</param>
        /// <param name="uniqueid">Unique Id</param>
        /// <returns>SharePoint List item</returns>
        private SPListItem RetrieveSPFarmTopologyListItem(SPList spFarmTopologyList, string uniqueid)
        {
            SPQuery query = new SPQuery();
            query.Query = String.Format(Global.listFilterQuery, uniqueid);
            SPListItemCollection spFarmTopologyListItems = spFarmTopologyList.GetItems(query);

            //Get the row based on the guid
            //If it doesnt exist, create a new row
            SPListItem spFarmTopologyListItem = spFarmTopologyListItems.Cast<SPListItem>().Any() ?
                                                spFarmTopologyListItems.Cast<SPListItem>().First() :
                                                spFarmTopologyList.Items.Add();

            return spFarmTopologyListItem;
        }
    }
}
